#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>

#include "codec.h"
#include "display.h"
#include "convert.h"
#include "zlink/ZLink.h"
#include "zlink/zlink_callback.h"


#define VIDEO_WIDTH					1280
#define VIDEO_HEIGHT				720

#define FB_LAYER_NUM				2

struct media_ctrler {
	int view_id;
	struct zlink_handle *handle;

	IHal_CodecHandle_t *impp_codec_handle;

	int impp_codec_srcbuf_num;
	int impp_codec_dstbuf_num;

	pthread_t get_video_tid;
	pthread_t decode_video_tid;

	int stopped;

	int display_need_key;

	int display_data_reay;
	pthread_mutex_t display_mutex;
};

static int view_id = 0;
static struct media_ctrler media_ctrlers[MAX_ZLINK_NUM];
static IHal_SampleFB_Handle_t *g_impp_samplefb_handle0;
static IHal_SampleFB_Handle_t *g_impp_samplefb_handle1;
static pthread_t render_tid0;
static pthread_t render_tid1;
static unsigned char display_data0[VIDEO_WIDTH * VIDEO_HEIGHT * 3] = {0};
static unsigned char display_data1[VIDEO_WIDTH * VIDEO_HEIGHT * 3] = {0};
static pthread_mutex_t g_display_mutex0;
static pthread_mutex_t g_display_mutex1;
// 【注意】：该全局互斥锁主要用于在多个线程同时调用`IHal_SimpleFB_UpdateWithAttr()`刷新Framebuffer数据时内核中出现死锁的问题。
static pthread_mutex_t g_display_update_mutex;

void *decode_video_thread(void *arg)
{
	int ret = 0;
	IHAL_CodecStreamInfo_t codec_dststream;
	struct media_ctrler *media_ctrler = (struct media_ctrler *)arg;
	struct tile420_fmt tile420_buf;
	struct nv12_fmt nv12_buf;

	while (!media_ctrler->stopped) {
		ret = IHal_Codec_WaitDstAvailable(media_ctrler->impp_codec_handle, IMPP_WAIT_FOREVER);
		assert(0 == ret);

		ret = IHal_Codec_DequeueDstBuffer(media_ctrler->impp_codec_handle, &codec_dststream);
		assert(0 == ret);

		tile420_buf.width = VIDEO_WIDTH;
		tile420_buf.stride = VIDEO_WIDTH;
		tile420_buf.height = VIDEO_HEIGHT;
		tile420_buf.y = codec_dststream.mp.vaddr[0];    // y数据起始地址
		tile420_buf.uv = codec_dststream.mp.vaddr[1];	// uv数据起始地址
		nv12_buf.width = VIDEO_WIDTH;
		nv12_buf.stride = VIDEO_WIDTH;
		switch (media_ctrler->view_id) {
			case 0:
				nv12_buf.height = VIDEO_HEIGHT * 2;
				nv12_buf.d = display_data0;
				pthread_mutex_lock(&g_display_mutex0);
				IHal_Convert_Tile420ToNV12(tile420_buf, &nv12_buf);
				pthread_mutex_unlock(&g_display_mutex0);
				break;
			case 1:
				nv12_buf.height = VIDEO_HEIGHT * 3 / 2;
				nv12_buf.d = (unsigned char *)display_data0 + VIDEO_WIDTH * VIDEO_HEIGHT;
				pthread_mutex_lock(&g_display_mutex0);
				IHal_Convert_Tile420ToNV12(tile420_buf, &nv12_buf);
				pthread_mutex_unlock(&g_display_mutex0);
				break;
			case 2:
				nv12_buf.height = VIDEO_HEIGHT * 2;
				nv12_buf.d = display_data1;
				pthread_mutex_lock(&g_display_mutex1);
				IHal_Convert_Tile420ToNV12(tile420_buf, &nv12_buf);
				pthread_mutex_unlock(&g_display_mutex1);
				break;
			case 3:
				nv12_buf.height = VIDEO_HEIGHT * 3 / 2;
				nv12_buf.d = (unsigned char *)display_data1 + VIDEO_WIDTH * VIDEO_HEIGHT;
				pthread_mutex_lock(&g_display_mutex1);
				IHal_Convert_Tile420ToNV12(tile420_buf, &nv12_buf);
				pthread_mutex_unlock(&g_display_mutex1);
				break;
		}

		ret = IHal_Codec_QueueDstBuffer(media_ctrler->impp_codec_handle, &codec_dststream);
		assert(0 == ret);
	}
}

void *render_thread(void *arg)
{
	int i = 0;
	int ret = 0;
	IMPP_BufferInfo_t fb_buf;
	int vsync = 0;
	IHal_SampleFB_LayerAttr_t layer_attr;
	IHal_SampleFB_Handle_t *impp_samplefb_handle = (IHal_SampleFB_Handle_t *)arg;

	memset(&layer_attr, 0, sizeof(IHal_SampleFB_LayerAttr_t));
	if (impp_samplefb_handle == g_impp_samplefb_handle0) {
		sprintf(&layer_attr.node[0], "/dev/fb0");
		layer_attr.osd_posX = 0;
		layer_attr.osd_order = Order_3;
	} else if(impp_samplefb_handle == g_impp_samplefb_handle1) {
		sprintf(&layer_attr.node[0], "/dev/fb1");
		layer_attr.osd_posX = 720;
		layer_attr.osd_order = Order_2;
	}
	layer_attr.lay_en = 1;
	layer_attr.srcFmt = IMPP_PIX_FMT_NV12;
	layer_attr.srcWidth = VIDEO_WIDTH;
	layer_attr.srcHeight = VIDEO_HEIGHT * 2;
	layer_attr.srcCropx = 0;
	layer_attr.srcCropy = 0;
	layer_attr.srcCropw = 0;
	layer_attr.srcCroph = 0;
	layer_attr.scale_enable = 1;
	layer_attr.scaleWidth = 640;
	layer_attr.scaleHeight = 720;
	layer_attr.osd_posY = 0;
	layer_attr.alpha = 255;

	while (1) {
		ret = IHal_SampleFB_WaitForVsync(impp_samplefb_handle, &vsync);
		assert(0 == ret);

		memset(&fb_buf, 0, sizeof(IMPP_BufferInfo_t));
		IHal_SampleFB_GetMem(impp_samplefb_handle, &fb_buf);

		// 调用`IHal_SimpleFB_Lock()`防止在Layer使用一个Buffer时上层和底层同时操作Buffer出现裂屏的问题
		IHal_SimpleFB_Lock(impp_samplefb_handle);
		if (impp_samplefb_handle == g_impp_samplefb_handle0) {
			pthread_mutex_lock(&g_display_mutex0);
			memcpy((void *)fb_buf.vaddr, display_data0, VIDEO_WIDTH * VIDEO_HEIGHT * 3);
			pthread_mutex_unlock(&g_display_mutex0);
		} else if (impp_samplefb_handle == g_impp_samplefb_handle1) {
			pthread_mutex_lock(&g_display_mutex1);
			memcpy((void *)fb_buf.vaddr, display_data1, VIDEO_WIDTH * VIDEO_HEIGHT * 3);
			pthread_mutex_unlock(&g_display_mutex1);
		}
		IHal_SimpleFB_UnLock(impp_samplefb_handle);

		pthread_mutex_lock(&g_display_update_mutex);
		IHal_SimpleFB_UpdateWithAttr(impp_samplefb_handle, &fb_buf, &layer_attr);
		pthread_mutex_unlock(&g_display_update_mutex);
	}
	pthread_exit(NULL);
}

void *get_video_thread(void *arg)
{
	int ret = 0;
	int packet_len = 0;
	int vsync = 0;
	char framedata[1024*1024] = {0};
	ZLink_FrameInfo frameInfo;
	IMPP_BufferInfo_t codec_srcbuf;
	struct media_ctrler *media_ctrler = (struct media_ctrler *)arg;
	IHAL_CodecStreamInfo_t codec_dststream;

	static unsigned int cnt = 0;
	static uint64_t start_time;

	uint64_t now_time = 0;

	ZLink_AV_Clean_Local_VideoBuf(media_ctrler->handle->avid);
	start_time = 0;
	cnt = 0;

	wait_packet_timeout(media_ctrler->handle, 5, -1);

	while (1) {
		wait_packet_timeout(media_ctrler->handle, 5, 1000 / VIDEO_FRAME_RATE);

		ret = ZLink_AV_Recv_FrameData(media_ctrler->handle->avid, framedata, sizeof(framedata), (char *)&frameInfo, sizeof(ZLink_FrameInfo), 5000);
		if (ret < 0) {
			fprintf(stderr, "failed to get video(%d) data\n", media_ctrler->view_id);
/*			*pkt = NULL;*/
			continue;
/*			return -11;*/
		}

		if (1 == media_ctrler->display_need_key && 1 != frameInfo.flags)
			continue;

		media_ctrler->display_need_key = 0;
		packet_len = ret;

		memset(&codec_srcbuf, 0, sizeof(IMPP_BufferInfo_t));

		/* decode nal units */
		ret = IHal_Codec_WaitSrcAvailable(media_ctrler->impp_codec_handle, IMPP_WAIT_FOREVER);
		assert(0 == ret);

		ret = IHal_Codec_DequeueSrcBuffer(media_ctrler->impp_codec_handle, &codec_srcbuf);
		assert(0 == ret);

		for (int i = 0; i < codec_srcbuf.mplane.numPlanes; i++) {
			memcpy((void *)codec_srcbuf.mplane.vaddr[i], (void *)framedata, packet_len);
		}
		codec_srcbuf.byteused = packet_len;

		ret = IHal_Codec_QueueSrcBuffer(media_ctrler->impp_codec_handle, &codec_srcbuf);
		assert(0 == ret);
	}
}

static IHal_CodecHandle_t *impp_codec_init(int view_id)
{
    int ret = 0;
    IHal_CodecHandle_t *impp_codec_handle = NULL;
    IHal_CodecParam h264dec_param;

    if (view_id < 0)
        return NULL;

	ret = IHal_CodecInit();
	assert(0 == ret);

	impp_codec_handle = IHal_CodecCreate(H264_DEC);
	assert(0 != impp_codec_handle);

	memset(&h264dec_param, 0, sizeof(IHal_CodecParam));
	h264dec_param.codec_type = H264_DEC;
	// h264dec_param.codecparam.h264d_param.need_split = ;
	h264dec_param.codecparam.h264d_param.src_width = VIDEO_WIDTH;					/* 源图像宽度 */
	h264dec_param.codecparam.h264d_param.src_height = VIDEO_HEIGHT;					/* 源图像高度 */
	h264dec_param.codecparam.h264d_param.dst_fmt = IMPP_PIX_FMT_JZ420B;				/* 目标像素格式 */
	ret = IHal_Codec_SetParams(impp_codec_handle, &h264dec_param);
	assert(0 == ret);

	return impp_codec_handle;
}

static IHal_SampleFB_Handle_t *impp_samplefb_init(int view_id)
{
    int ret = 0;
	IHal_SampleFB_LayerAttr_t layer_attr;
    IHal_SampleFB_Handle_t *impp_samplefb_handle;

    if (view_id < 0)
        return NULL;

	memset(&layer_attr, 0, sizeof(IHal_SampleFB_LayerAttr_t));
	sprintf(&layer_attr.node[0], "/dev/fb%d", view_id);
	layer_attr.lay_en = 1;
	layer_attr.srcFmt = IMPP_PIX_FMT_NV12;
	layer_attr.srcWidth = VIDEO_WIDTH;
	layer_attr.srcHeight = VIDEO_HEIGHT * 2;
	layer_attr.srcCropx = 0;
	layer_attr.srcCropy = 0;
	layer_attr.srcCropw = 0;
	layer_attr.srcCroph = 0;
	layer_attr.scale_enable = 1;
	layer_attr.scaleWidth = 640;
	layer_attr.scaleHeight = 720;
	layer_attr.osd_posX = view_id * 720;
	layer_attr.osd_posY = 0;
	if (0 == view_id)
		layer_attr.osd_order = Order_3;
	else if (1 == view_id)
		layer_attr.osd_order = Order_2;
	layer_attr.alpha = 255;

	impp_samplefb_handle = IHal_SimpleFB_Init(&layer_attr);
	assert(NULL != impp_samplefb_handle);

    return impp_samplefb_handle;
}

int main()
{
	int ret = 0;
	IHal_CodecParam h264dec_param;
	int srcbuf_nums = 0;
	int dstbuf_nums = 0;
	IHal_SampleFB_Attr fb_attr;
	IMPP_BufferInfo_t fb0_buf;

	init_zlink_media();

	g_impp_samplefb_handle0 = impp_samplefb_init(0);
	g_impp_samplefb_handle1 = impp_samplefb_init(1);

	pthread_mutex_init(&g_display_mutex0, NULL);
	pthread_mutex_init(&g_display_mutex1, NULL);
	pthread_mutex_init(&g_display_update_mutex, NULL);

	ret = pthread_create(&render_tid0, NULL, render_thread, g_impp_samplefb_handle0);
	assert(0 == ret);

	ret = pthread_create(&render_tid1, NULL, render_thread, g_impp_samplefb_handle1);
	assert(0 == ret);

	while (1) {
		media_ctrlers[view_id].handle = open_zlink_handle(NULL);
		if (!media_ctrlers[view_id].handle)
			continue;

		media_ctrlers[view_id].view_id = view_id;
		media_ctrlers[view_id].display_need_key = 1;
		media_ctrlers[view_id].display_data_reay = 0;
		pthread_mutex_init(&media_ctrlers[view_id].display_mutex, NULL);

		media_ctrlers[view_id].impp_codec_handle = impp_codec_init(view_id);

		media_ctrlers[view_id].impp_codec_srcbuf_num = IHal_Codec_CreateSrcBuffers(media_ctrlers[view_id].impp_codec_handle, IMPP_INTERNAL_BUFFER, 3);
		assert(1 <= media_ctrlers[view_id].impp_codec_srcbuf_num);

		media_ctrlers[view_id].impp_codec_dstbuf_num = IHal_Codec_CreateDstBuffer(media_ctrlers[view_id].impp_codec_handle, IMPP_INTERNAL_BUFFER, 3);
		assert(1 <= media_ctrlers[view_id].impp_codec_dstbuf_num);

		ret = IHal_Codec_Start(media_ctrlers[view_id].impp_codec_handle);
		assert(0 == ret);


		ret = pthread_create(&media_ctrlers[view_id].get_video_tid, NULL, get_video_thread, &media_ctrlers[view_id]);
		assert(0 == ret);

		ret = pthread_create(&media_ctrlers[view_id].decode_video_tid, NULL, decode_video_thread, &media_ctrlers[view_id]);
		assert(0 == ret);

		if (++view_id >= 4)		/* 0 1 : 双路 ; 0 1 2 3 : 四路*/
			break;
	}


	while (1) {
		sleep(1);
	}

	return 0;
}
